home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1987 / 10 / brownlst.oct < prev    next >
Text File  |  1987-09-14  |  87KB  |  1,983 lines

  1. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2. *
  3. *   THIS FILE CONTAINS EXCERPTS FROM THE APPLETALK SOURCES, 
  4. *     VERSION 39, AUGUST 1985, AS MODIFIED BY DARTMOUTH COLLEGE
  5. *    TO PRODUCE THE ASYNC APPLETALK DRIVER (.BPP) VERSION 1.2
  6. *    (ASYNC APPLETALK INSTALLER VERSION 2.1) OF MAY 1987.
  7. *
  8. *     THESE EXCERPTS CONTAIN INFORMATION OF TWO TYPES:
  9. *        1) CODE WRITTEN ENTIRELY AT DARTMOUTH COLLEGE;
  10. *        2) CODE WHICH IS FUNDAMENTALLY SIMILAR TO THE
  11. *            PRELIMINARY APPLETALK SOFTWARE DISTRIBUTED
  12. *            WITHOUT RESTRICTION AT THE APPLEBUS DEVELOPER'S
  13. *            CONFERENCE IN CUPERTINO, CA IN MAY, 1984.
  14. *
  15. *    PORTIONS OF THIS CODE ARE COPYRIGHT OF THE TRUSTEES OF
  16. *    DARTMOUTH COLLEGE OR APPLE COMPUTER INC.
  17. *
  18. *    THESE CODE SEGMENTS ARE PROVIDED FOR INFORMATION ONLY.  NO
  19. *    GUARANTEE OF CORRECT OPERATION IS PROVIDED.
  20. *
  21. *    FOR MORE INFORMATION ABOUT THIS CODE, CONTACT:
  22. *
  23. *    Rich Brown
  24. *    Manager of Special Projects
  25. *    Dartmouth College
  26. *    Kiewit Computer Center
  27. *    Hanover, NH 03755
  28. *    603/646-3648
  29. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  30.  
  31. ***** file ALAPDEFS.A *****
  32.  
  33. ; AALAPdefs.a contains all the special definitions which were not
  34. ; needed for .MPP .
  35. ;
  36. ; .BPP et al should now use unmodified versions of:
  37. ;        {AIncludes}atalkequ.a
  38. ;        lapdefs.a
  39. ;        vardefs.a
  40. ;                            
  41. ; Created 31 Mar 87   reb 
  42. ;
  43. ; AALAP constant defs
  44. ;
  45. MaxLAPFrmLen EQU    586+13+3+2          ; DDP data + DDP hdr + LAP hdr + CRC
  46. FrameChar   EQU     $A5                 ; the Framing Char
  47. qFrmChar    EQU     -91                 ; for moveq instructions
  48. DLE         EQU     $10
  49. Xoff        EQU     $13
  50. Xon         EQU     $11
  51.  
  52. lapIM       EQU     $86                 ; I aM
  53. lapUR       EQU     $87                 ; yoU aRe (sorry for these names...)
  54. qlapIM      EQU     -122
  55. qlapUR      EQU     -121
  56.  
  57. noansalrt   EQU     -15998
  58. portncalrt  EQU     -15997
  59.  
  60. ; Added constant return value for AALAP --
  61. noAnswer        EQU     -95          ; same as excessCollsns in real AtalkEqu 
  62.  
  63. AAOfst          EQU      10000      ; 0 or 10000 (for final version)
  64.  
  65. ;+ MPP (Status calls to NBP, DDP and AALAP)
  66.  
  67. GetStats        EQU      400        ; (ABLAP) get the statistics 
  68. GetMyName       EQU      AAOfst+255 ; get the name of the ATalk driver
  69. (AALAP)
  70. GetChar         EQU      AAOfst+254 ; get the most recently received char
  71. (AALAP)
  72. GetLAPStatus    EQU      AAOfst+253 ; return AALAP status  (AALAP)
  73.  
  74. ;+ MPP (Control calls to NBP, DDP, and AALAP)
  75.  
  76. FirstAPP        EQU      AAOfst+237 ; First APP control call
  77. DoWarnings      EQU      AAOfst+237 ; Put up the specified alerts
  78. (AALAP)
  79. PutChar         EQU      AAOfst+238 ; Loop 'til TBMT, then output the char
  80. (AALAP)
  81. ReInitAALAP     EQU      AAOfst+239 ; ReInitialize the AALAP variables & SCC
  82. (AALAP)
  83. GetNNNN         EQU      AAOfst+240 ; Do NNNN using SysNetNum and sysLAPAddr
  84. (AALAP)
  85. SetBaud         EQU      AAOfst+241 ; Set the baud rate of the SCC
  86. (AALAP)
  87. LastAPP         EQU      AAOfst+241
  88.           EJECT
  89. ;
  90. ; LAP variables
  91. ;
  92. WDSPtr      EQU     MPPVarsEnd          ; (4) WDS pointer saved here on writes
  93. LAPWrtRtn   EQU     WDSptr+4            ; (4) return adrs of LAPWrite caller 
  94. SaveA45     EQU     LAPWrtRtn+4         ; (8) A4 and A5 saved here on interrupt
  95. SaveDskRtn  EQU     SaveA45+8           ; (4) DskRtnAdr saved here for
  96. PollProc
  97. SavePS      EQU     SaveDskRtn+4        ; (4) in AALAP, the real PollProc's
  98. address
  99. SaveBIn     EQU     SavePS+4            ; (4) .BIN DCE saved here (for close)
  100. SaveBOut    EQU     SaveBIn+4           ; (4) .BOUT DCE saved here (for close)
  101. SaveVects   EQU     SaveBOut+4          ; (16) SCC interrupt vectors saved
  102. here
  103. SaveRegs    EQU     SaveVects+16        ; (20) Registers saved here across
  104. PollProc
  105.  
  106. ;
  107. ; Variables for Lisa/Mac hardware differences
  108. ;
  109. VAVBufA     EQU     SaveRegs+20         ; Pointer to VIA or a $FF word
  110. STLth       EQU     6                   ; Size of STData area
  111. VSTData     EQU     VAVBufA+4           ; Data string to SCC after send
  112. VDisTxRTS   EQU     VSTData+1           ; This is the DisTxRTS byte
  113. EndOrigStuff EQU    VSTData+STLth       ;
  114.  
  115. ;
  116. ; AALAP varibles
  117. ;
  118. tWDSptr     EQU     EndOrigStuff+2      ; (4) WDS ptr of frame being tx
  119. qWDSptr     EQU     tWDSptr+4           ; (4) WDS of a queued DevMgr frame
  120. LastXmit    EQU     qWDSptr+4           ; (4) Ticks at time of last char sent
  121. LastRcv     EQU     LastXmit+4          ; (4) Ticks at time of last good
  122. received frame
  123. LAPStash    EQU     LastRcv+4           ; (4) Pointer to next received char's
  124. place
  125. LAPFetch    EQU     LAPStash+4          ; (4) Pointer to next char to xmit
  126. LAPInBuf    EQU     LapFetch+4          ; (4) Pointer to the LAP input buffer
  127. IMURwds     EQU     LAPInBUf+4          ; (8) WDS for IM or UR frames
  128. BusyBuf     EQU     IMURwds+8           ; (16) Holds up to 16 chars rcvd while
  129. doingRead
  130. BusyStash   EQU     BusyBuf+16          ; (4) pointer to next space in BusyBuf
  131. BusyFetch   EQU     BusyStash+4         ; (4) pointer to next char to remove
  132. IMURbuf     EQU     BusyFetch+4         ; (8) Holds IM or UR (starting at odd
  133. adrs)
  134. InputCRC    EQU     IMURBuf+8           ; (2) CRC for the receiver
  135. OutputCRC   EQU     InputCRC+2          ; (2) CRC for the transmit side
  136. RcvdLen     EQU     OutputCRC+2         ; (2) Number of chars received
  137. TxCount     EQU     RcvdLen+2           ; (2) Number of char's transmitted
  138. CRCBuf      EQU     TxCount+2           ; (2) Two bytes for the CRC for
  139. xmission
  140. RandomSeed  EQU     CRCBuf+2            ; (2) Seed for random number generator
  141. LastRxCh    EQU     RandomSeed+2        ; (2) Lsbyte is last rcvd char, else
  142. $FFFF
  143. AALAPbaud   EQU     LastRxCh+2          ; (2) Current baud rate of the LAP
  144. SentChar    EQU     AALAPbaud+2         ; (1) True if TxNextCh sent a char
  145. nFrmChr     EQU     SentChar+1          ; (1) True if we must send a FrameChar
  146. nCRC        EQU     nFrmChr+1           ; (1) True if we must send the CRC
  147. EscIn       EQU     nCRC+1              ; (1) Escaping flag for the receiver
  148. EscOut      EQU     EscIn+1             ; (1) Transmitter is sending an escaped
  149. char
  150. RcvdXoff    EQU     EscOut+1            ; (1) We received Xoff
  151. AALAPup     EQU     RcvdXoff+1          ; (1) true if we've handshook IM & UR
  152. AALAPstuck  EQU     AALAPup+1           ; (1) true if we have NNNN conflict
  153. InpState    EQU     AALAPstuck+1        ; (1) 0 = idle; <> 0 = in a frame
  154. stillBusy   EQU     InpState+1          ; (1) true if still processing a read
  155. nXon        EQU     stillBusy+1         ; (1) true if we sent Xoff
  156. SendingIMUR EQU     nXon+1              ; (1) true if sending AALAP control frame
  157.  
  158. _AssumeEq   (InpState+1),stillBusy         ; tst.w InpState(A4) in 
  159. _AssumeEq   (InpState**$FFFFFFFE),InpState ;   myPollProc fails otherwise
  160.  
  161.             IF      debug THEN          ; doing statistics
  162. XmitCount   EQU     SendingIMUR+1
  163. XOFFTOcount EQU     XmitCount+4
  164. OVRcount    EQU     XOFFTOcount+4
  165. RcvIntCount EQU     OVRcount+4
  166. XOFFcount   EQU     RcvIntCount+4
  167. XONcount    EQU     XOFFcount+4
  168. LongFrame   EQU     XONcount+4
  169. ShortFrame  EQU     LongFrame+4
  170. FrmCount    EQU     ShortFrame+4
  171. NoHandCnt   EQU     FrmCount+4
  172. CRCCount    EQU     NoHandCnt+4
  173. LenErrCnt   EQU     CRCCount+4
  174. BadDDP      EQU     LenErrCnt+4
  175. PPCount     EQU     BadDDP+4
  176. PPXoffCnt   EQU      PPCount+4
  177. DeferXmit   EQU      PPXoffCnt+4
  178. ABVarsEnd   EQU     DeferXmit+4
  179.             ELSE 
  180. ABVarsEnd   EQU     SendingIMUR+1        ; end of AALAP variables
  181.             ENDIF
  182.  
  183.  
  184. ***** file MPP.A *****
  185.  
  186. ... section removed ...
  187.  
  188. ;___________________________________________________________________________
  189. ;
  190. ; SCCConfig - set up the SCC for AppleBus
  191. ;___________________________________________________________________________
  192.  
  193. SCCConfig   LEA     OpenTbl,A0          ; A0 -> (common) open table
  194.             CMP.B   #$FF,MacTypeByte    ; Mac or Lisa?
  195.             BNE.S   @10                 ; Branch if Mac - configure it
  196.             BSR     ToSCC               ; Configure SCC to major settings
  197.             LEA     LOpenTbl,A0         ; A0 -> Lisa open table
  198. @10         BRA     ToSCC               ; Configure SCC and return
  199.  
  200. ... section removed ...
  201.  
  202. ToSCC       MOVE.L  SCCWr,A3            ; Point to SCC port B write registers
  203.             IF      PortA THEN
  204.             ADDQ    #ACtl,A3            ; Add in port A offset
  205.             ENDIF
  206. @10         MOVE    (A0)+,D0            ; Get next register number / control
  207. word
  208.             BEQ.S   CloseRTS            ; Zero is terminator
  209.             MOVE.B  D0,(A3)             ; Put out register number
  210.             ROR     #8,D0               ; Pickup control word
  211.             MOVE.B  D0,(A3)             ; Set to SCC
  212.             BRA.S   @10                 ; And keep going
  213.  
  214. ... section removed ...
  215.  
  216. ;_______________________
  217. ;
  218. ; Initialization tables
  219. ;_______________________
  220.  
  221. ;
  222. ; SCC Initialization table - common between Mac and Lisa
  223. ; Entry format: .BYTE control-value, control-reg-number
  224. ; Taken from the Zilog SCC Application note, 00-2957-02
  225.  
  226. OpenTbl     DC.B    ResetOurPort,9      ; ($40 or $80) Reset port
  227.             DC.B    $44,4               ; x16 clock, 1 stop, no parity
  228.             DC.B    $0,2                ; Interrupt vector = $00
  229.             DC.B    $C0,3               ; Rx is 8 bits, disable Rx
  230.             DC.B    $E2,5               ; Tx is 8 bits, Disable Tx; DTR, RTS
  231. on
  232.             DC.B    $0,6                ; No address
  233.             DC.B    $0,7                ; No Flag character
  234.             DC.B    $0,10               ; NRZ
  235.             DC.B    $56,11              ; Tx & Rx clock from BRG
  236.             DC.B    $2,14               ; BRG source = PCLK, BRG off
  237. ; enables
  238.             DC.B    $3,14               ; BRG on
  239.             DC.B    $C1,3               ; Rx on
  240.             DC.B    $EA,5               ; Tx on
  241. ; Interrupt controls
  242.             DC.B    MouseInts,15        ; enable DCD ints (for mouse)
  243.             DC.B    $10,0               ; reset external ints
  244.             DC.B    $10,0               ; reset external ints (twice)
  245.             DC.B    $13,1               ; Tx, Rx, Ext int enable
  246.             DC.B    MIE,9               ; Master Interrupt Enable
  247.             DC.W    0                   ; *** End of table ***
  248.  
  249.             IF      RAM THEN            ; Only need Lisa table if RAM-based
  250. ;
  251. ; SCC initialization table for Lisa
  252. ; Port A uses PCLK (@4.0 MHz TTL) to drive BRG; Port B uses 3.6864 MHz Xtal
  253. ;
  254.             IF      PortA THEN          ; configuration for Port A
  255. LOpenTbl    DC.B    $00,14              ; turn off BRG
  256.             DC.B    $6A,5               ; enable TX, RTS; DTR low
  257.             DC.B    $56,11              ; TTL clock, tx and rx use BRG
  258.             DC.B    $02,14              ; Use PCLK to feed BRG, BRG off
  259.             DC.B    $03,14              ; BRG on
  260.             DC.W    0
  261.             ELSE                        ; configuration for PortB
  262. LOpenTbl    DC.B    $00,14              ; turn off BRG
  263.             DC.B    $6A,5               ; enable TX, RTS; DTR low
  264.             DC.B    $D6,11              ; Crystal clock, tx and rx use BRG
  265.             DC.B    $00,14              ; Use crystal to feed BRG, BRG off
  266.             DC.B    $01,14              ; BRG on
  267.             DC.W    0
  268.             ENDIF
  269.  
  270. ;
  271. ; Mac initialization data (first is the post-transmitting SCC string)
  272. ;
  273. MacInitData DC.B    5,MDisTxRTS         ; ($60) Turn off drivers
  274.             DC.B    14,ResetClks        ; ($41) Reset missing clocks flag
  275.             DC.B    3,EnbRxSlv          ; ($DD) Enable receiver
  276.             DC.W    $2100               ; SR to enable SCC interrupts
  277.             DC.B    AbortDelay,0        ; Delay to send out abort bits (3.2B)
  278.             IF      RAM THEN
  279. ;
  280. ; Lisa initialization data
  281. ;
  282. LisaInitData DC.B   5,LDisTxRTS         ; ($E2) Turn off drivers
  283.             DC.B    14,ResetClks        ; ($41) Reset missing clocks flag
  284.             DC.B    3,EnbRxSlv          ; ($DD) Enable receiver
  285.             DC.W    $2500               ; SR to enable SCC interrupts
  286.             DC.B    34,0                ; Just delay this much on Lisa (3.2B)
  287.             ENDIF
  288.  
  289. ***** file LAP.A *****
  290.  
  291. ;_______________________________________________________________________
  292. ;
  293. ; LAP.TEXT - the LAP part of AALAP
  294. ;
  295. ; April-August, 1984
  296. ; Alan Oppenheimer and Larry Kenyon
  297. ;
  298. ; Rich Brown, Dartmouth College
  299. ; May 1987
  300. ;
  301. ; Version 1.2a6 Created qWDSptr to point at queued WDS  21 May 87   reb
  302. ; Version 1.2a5 Always check that TBMT is true before sending  19 May 87   reb
  303. ; Version 1.2a4 TintHnd, VBLHnd, RintHnd now call TxNextCh; only TintHnd
  304. ; clears interrupts (as it should be)   14 May 87   reb
  305. ; Version 1.2a3 Prefetching warning dialogs doesn't work; backed out  10 May 87 reb
  306. ; Version 1.2a2 tWDSptr now determines whether we're sending a frame;
  307. ; DoWarn now doesn't read the resource file  8 May 87   reb
  308. ; Version 1.2a1 Removed queueing from LAPWrite.  LAPWrite no longer 
  309. ; allocates memory, so it won't fail if called from
  310. ; interrupt handling.   19 Apr 87   reb
  311. ; Version 1.1b2 Changed noAnswer to -95 (so it can be handled like excessCollsns)
  312. ; LAPWrite returns noAnswer if AALAP not up; (30 Mar 87)
  313. ; GetNNNN  returns noAnswer or PortNotCF;
  314. ; Changed LAPWrite to return ddpLenErr if too long
  315. ; Version 1.1b1 Fixed PollProc to be more agressive about sucking chars
  316. ; from the SCC; added -1 SendChar value (sends Break);
  317. ; fix Initcursor bug in Dowarn 16 & 30 Dec 86   reb
  318. ; Version 1.1a1 Output an Xoff if called by PollProc during an input message
  319. ; 3 Nov 86   reb
  320. ; Version 1.0b2    Changed to set up SCC properly for Lisa  15 Oct 86
  321. ; (still has intermittent hangups, tho -- not diagnosed)
  322. ; Version 1.0b1 Changed last_valid_frame timer to 30 seconds; always send
  323. ; UR, even after un-matchable IM address
  324. ; Version 1.0a3 Fixed Status return buffer bug; SetBaud now takes actual
  325. ;                   baud rate; added GetLAPStatus call; copy entire message
  326. ; Version 1.0A2 Added alerts for NoAnswer, PortNotCf   (17 Jul 86   reb)
  327. ; Version 4.2   Int handlers now do IUS etc. more carefully (4 Jul 86)
  328. ; Version 4.1   Now escapes either parity Xon and Xoff (21 Apr 86)
  329. ; Version 4.0   First cut at AALAP (26 Oct-14 Dec 85)
  330. ... section removed ...
  331. ;
  332. ; COPYRIGHT (C) 1984 APPLE COMPUTER
  333. ;_______________________________________________________________________
  334.  
  335. ... section removed ...
  336.  
  337. ;___________________________________________________________________________
  338. ;
  339. ; MReInit - Control call to reinit AALAP and the SCC
  340. ;___________________________________________________________________________
  341.  
  342. MReInit     bsr.s AALAPWarm           ; Warm start ourselves
  343.             BRA   AbusExit            ; and return
  344. ;___________________________________________________________________________
  345. ;
  346. ; AALAPCold -- cold start for AALAP; called only once
  347. ;___________________________________________________________________________
  348.  
  349. AALAPCold   
  350. ;
  351. ; Allocate the input buffer (This should be alloc above BufPtr, not sysheap)
  352. ;
  353.             move.l  #maxLAPFrmLen,D0    ; get an AALAP input buffer
  354.             _newptr ,SYS                ; from the system heap
  355.             bne.s  WarmRTS              ; exit if bad
  356.             move.l  A0,LAPInBuf(a2)     ; otherwise save its pointer
  357. ;
  358. ; Clear out LAP variables
  359. ;
  360.             clr.l   WDSPtr(a2)
  361.             clr.l   tWDSptr(A2)
  362.             clr.l   LAPWrtRtn(A2)
  363.             clr.w   SysNetNum(a2)
  364.             clr.b   SysLAPAddr(a2)
  365.             clr.l   SavePS(A2)
  366.             sf      AALAPup(a2)
  367.             sf      AALAPstuck(a2)
  368. ;
  369. ; Setup SCC for AALAP
  370. ;
  371.             BSR     SCCConfig           ; Configure the SCC for Async
  372. AppleTalk
  373.             move    #9600,D0            ; and set up for 9600 baud
  374.             bsr      Set_Baud           ; 
  375. ;
  376. ; Reset all the LAP variables which don't irrevocably change the 
  377. ; state of the driver.  This routine can be called any time, only 
  378. ; killing the current message(s) in progress.
  379. ;
  380. AALAPWarm   move.l  Ticks,D0
  381.             move.l  D0,LastXmit(a2)
  382.             move.l  D0,LastRcv(a2)
  383.             lea     BusyBuf(a2),A0
  384.             move.l  A0,BusyStash(a2)
  385.             move.l  A0,BusyFetch(a2)
  386.             move.w  #$FFFF,LastRxCh(a2)
  387.             clr.l   tWDSptr(A2)
  388.             clr.l   qWDSptr(A2)
  389.             clr.w   TxCount(a2)
  390.             sf      RcvdXoff(a2)
  391.             sf      InpState(a2)
  392.             sf      EscIn(a2)
  393.             sf      SendingIMUR(A2)
  394.             sf      stillBusy(a2)
  395.             sf      nFrmChr(a2)
  396.             sf      nCRC(a2)
  397.             sf      nXon(A2)
  398. WarmRTS     rts
  399.  
  400.             EJECT
  401. ;___________________________________________________________________________
  402. ;
  403. ; Status - handle driver status request
  404. ;___________________________________________________________________________
  405.            _SUBR                        ; no one better call this...
  406. Status      MOVE.L  MPPVars,A2          ; A2 -> our variables
  407.             MOVEQ   #StatusErr,D0       ; Assume a status error
  408.             lea     CSParam(A0),A1      ; point at the CSParam buffer
  409.             move.w  CSCode(A0),D1       ; and get the CScode
  410.  
  411.             IF      Stats THEN
  412.             CMP.W   #GetStats,D1        ; Clear stats command?
  413.             BNE.S   @1                  ; check for "What's my Name?" if not
  414.             move.w  CSParam(A0),A1      ; CSParam contains a pointer to buffer
  415.             MOVE    SR,-(SP)
  416.             MOVE    #SCCLockout,SR      ; exclude interrupts to keep stats
  417. clean
  418.             ADD     #StatsStart,A2      ; point to stats we keep
  419.             MOVEQ   #(StatsLgCnt-1),D0
  420.             MOVEQ   #0,D1               ; zero for faster clearing
  421. @0          MOVE.L  (A2),(A1)+          ; return current value
  422.             MOVE.L  D1,(A2)+            ; then zero count
  423.             DBRA    D0,@0
  424.             MOVE    (SP)+,SR
  425.             bra.s   AbusExit
  426.             ENDIF
  427.  
  428. @1          cmp.w   #GetMyName,D1       ; is this a "what's my name" call?
  429.             bne.s   @2                  ; go if not
  430.             Move.l  MPP+18,(A1)+        ; move Pascal string from front of driver
  431.             move.b  MPP+22,(A1)         ; to beginning the buffer (5 chars)
  432.             bra.s   @4                  ; and exit with good status
  433.  
  434. @2          cmp.w   #GetChar,D1         ; is this a "get last char" call?
  435.             bne.s   @3                  ; go if not
  436.             move.w  LastRxCh(a2),(A1)   ; copy the character (word)
  437.             move.w  #$FFFF,LastRxCh(a2) ; and flag the character
  438.             bra.s   @4
  439.  
  440. @3          cmp.w   #GetLAPStatus,D1    ; is this a "get LAP status" call?
  441.             bne.s   AbusExit            ; go if not
  442.             move.b  AALAPup(A2),(A1)+   ; AALAPup?
  443.             move.b  AALAPstuck(A2),(A1)+;AALAPstuck?
  444.             move.w  AALAPbaud(A2),(A1)  ; What's the baud rate?
  445. @4          clr.l   D0                  ; return good status
  446.  
  447. AbusExit    MOVE.L  MPPDCE,A1           ; Make sure A1 has DCE address
  448. AbusExA1    MOVE.L  JIODone,-(SP)       ; This is how we exit (Prime, Control,
  449. Status)
  450. AbusRTS     RTS
  451.             SUBEND 'MYSTATUS'           ; this marks the AbusExit
  452.  
  453. Prime       BCLR    #DrvrActive,DCtlFlags+1(A1) ; *** V2.0C Fix Mac ROM bug
  454. ***
  455.             RTS                                 ; *** V2.0C Fix Mac ROM bug
  456. ***
  457.             EJECT
  458. ;_____________________________________________________________________
  459. ;
  460. ; MGetNNNN -- Do the NNNN, using the current values of SysLAPAddr and
  461. ;             SysNetNum.  Return bad status if it didn't work.
  462. ;
  463. ; On entry: A2 -> BPP variables
  464. ; On exit:  D0 = noErr (0) if we succeeded,
  465. ;                PortNotCF (-98) or
  466. ;                noAnswer  (-95) if not
  467. ;____________________________________________________________________
  468.  
  469. MGetNNNN    bsr.s   Get_NNNN            ; Use them just as they are
  470.             bra.s   AbusExit            ; return from the control calls
  471.  
  472. tries       EQU     -2                  ; counter for the tries
  473. endtime     EQU     -6                  ; end time
  474.  
  475. Get_NNNN    _SUBR   6
  476.             move.w  Ticks+2,RandomSeed(a2) ; randomize things
  477.             move.b  SysLAPAddr(a2),D0   ; Node number in D0
  478.             move    SysNetNum(a2),D1    ; Net number in D1
  479.             sf      AALAPup(a2)         ; we're not up yet
  480.             sf      AALAPstuck(a2)      ; and we're not in trouble either
  481.             move    #4,tries(a6)        ; tries counter (4 tries)
  482. @10         move.l  Ticks,D2
  483.             add.l   #30,D2              ; set endtime to the current time+30
  484.             move.l  D2,endtime(a6)      ; remember the ending time
  485.             moveq   #qlapIM,D2          ; get the lap type
  486.             move.w  SysNetNum(a2),D1    ; get the Net number
  487.             move.b  SysLAPAddr(a2),D0   ; and the node number
  488.             bsr     SendIMUR            ; and send it
  489. @20         clr     D0                  ; good status if things are OK
  490.             tst.b   AALAPup(a2)         ; did the magic work?
  491.             bne.s   NNNNexit            ; go if so
  492.             tst.b   AALAPstuck(a2)      ; is there an irreconcilable difference?
  493.             bne.s   NNNNstuck
  494.             move.l  endtime(a6),D2
  495.             cmp.l   Ticks,D2            ; otherwise, check the timer
  496.             bpl.s   @20                 ; loop if not timed out
  497.             sub     #1,tries(a6)        ; decr the counter
  498.             bgt.s   @10                 ; loop if non-zero
  499.             moveq   #noAnswer,D0        ; They don't want to talk
  500.             bra.s   NNNNexit
  501.  
  502. NNNNstuck   moveq   #PortNotCF,D0       ; They talk but say bad things
  503.  
  504. NNNNexit    tst.w   D0                  ; set CC
  505.             _Subend 'GETNNNN '          ; and return
  506.             EJECT
  507. ;___________________________________________________________________________
  508. ;
  509. ; MPutChar -- Kill output and send the char pointed to in the control call
  510. ;
  511. ; Entry:    A0 -> IOQelement
  512. ; Exit:     Return status is 0000   if noErr,
  513. ;                            BadIO  if timed out waiting for TBMT
  514. ;___________________________________________________________________________
  515.  
  516. SendBrk     equ     $12                 ; Sends Break (w/RTS) when sent to WR5
  517.  
  518. MPutChar    bsr.s   Put_Char
  519.             bra     AbusExit            ; and exit
  520.  
  521. Put_Char     _SUBR
  522.             move.w  CSParam(a0),D0      ; get the character (in an integer)
  523.             bmi.s   @10                 ; if it's 0..255, 
  524.             bsr     SendChar            ;    output the character
  525.             bra.s   @20                 ; and quit
  526.  
  527. @10         lea     BreakTbl,A0         ; 
  528.             move.b  #SendBrk,(A0)       ; set the break bit in WR5
  529.             bsr     ToScc               ; 
  530.             move.l  #10,A0              ; wait 10 ticks
  531.             _delay
  532.             move    #$EA,D0             ; Enable Tx, DTR, RTS
  533.             CMPI.B  #$FF,MacTypeByte    ; Mac or Lisa?
  534.             BNE.S   @15                 ; Branch if Mac (PortA & PortB are same)
  535.             move    #$6A,D0                 ; Lisa doesn't assert DTR
  536.             @15lea  BreakTbl,A0
  537.             move.b  D0,(A0)               ; and turn the Break off
  538.             bsr     ToSCC
  539.             clr     D0
  540.  
  541. @20         SUBEND 'MPUTCHAR'
  542.  
  543. breaktbl    dc.b    0,5      ; THIS WON'T MAKE ROMMABLE CODE
  544.             dc.w    0000
  545. ;_____________________________________________________________________
  546. ;
  547. ;  MSetBaud -- send the (integer) value in the CSParamblk to SCC as its Baud
  548. Rate
  549. ;  Entry:  A0 -> IOQelement
  550. ;  Exit:   noErr if aok
  551. ;          -1    if requesting 19,200 baud on a Lisa, port A (cannot be done)
  552. ;_____________________________________________________________________
  553. ; THIS WON'T MAKE ROMMABLE CODE!
  554. BaudConsts  DC.B    2,14                ; turn off BRG (so it doesn't count for
  555. a while)
  556. lsBaudVal   DC.B    0,12                ; LSByte of BRG
  557. msBaudVal   DC.B    0,13                ; MSByte of BRG
  558. BaudSrc     DC.B    0,14                ; turn it on again, with proper baud
  559. source
  560.             DC.W    0000                ; end of constant string
  561.  
  562. BaudTable   DC.W    1200,94,102         ; 1200 baud, Mac&LisaB , LisaA BRG
  563. constants
  564.             DC.W    2400,46,50          ; 2400 baud
  565.             DC.W    4800,22,24          ; 4800 baud
  566.             DC.W    9600,10,11          ; 9600 baud
  567.             DC.W    19200,4,-1          ; 19200 baud (but not for Lisa Port
  568. A...)
  569. BaudTblEnd  DC.W    -1                  ; sentinel
  570.  
  571. MSetBaud    move    CSParam(a0),D0      ; get the (integer) baud rate
  572.             bsr.s   Set_Baud
  573.             bra     AbusExit
  574.  
  575. Set_Baud    _SUBR                       ; D0 contains the actual desired baud
  576. rate
  577.             move.w  D0,AALAPbaud(A2)    ; save the current baud rate
  578.             lea     BaudTable,A0        ; point at the table
  579. @10         cmp.w   (a0),D0             ; does it match?
  580.             beq.s   @30                 ; go if so
  581.             addq.l  #6,A0
  582.             tst.w   (A0)                ; are we done?
  583.             bpl.s   @10                 ; loop if we didn't hit the sentinel
  584.             moveq   #-1,D0              ;
  585.             bra.s   @50                 ; and bail out
  586.  
  587. @30         moveq   #3,D1               ; set up for Mac port A/B (PCLK/BRG on)
  588.             CMPI.B  #$FF,MacTypeByte    ; Mac or Lisa?
  589.             BNE.S   @40                 ; Branch if Mac (PortA & PortB are
  590. same)
  591.          IF  PortA THEN                 ; Lisa ports A/B differ; Macs don't
  592.             addq.l  #2,A0               ; bump to Lisa PortA column
  593.          ELSE
  594.             moveq   #1,D1               ; Lisa portB works from Xtal, not PCLK
  595.          ENDIF
  596. @40         move.w  2(a0),D0            ; get value BRG (-1 if 19,200 on Lisa)
  597.             bmi.s   @50                 ; exit if negative
  598.  
  599. ; D0 now contains the value for the BRG
  600.             lea     BaudConsts,a0       ; point at the constants
  601.             move.b  d0,lsBaudVal-BaudConsts(a0) ; save the LSByte of the BRG
  602.             ror     #8,d0
  603.             move.b  d0,msBaudVal-BaudConsts(a0) ; and the MSByte of the BRG
  604.             move.b  d1,BaudSrc-BaudConsts(a0)   ; and the source for BRG
  605.             bsr     toSCC               ; and output it
  606.             clr     d0
  607. @50         _SUBEND 'MSETBAUD'
  608.             EJECT
  609. ;___________________________________________________
  610. ;
  611. ; MWriteLAP - write out a LAP packet
  612. ;
  613. ;   Call:
  614. ;       A0 -> IO queue element
  615. ;       A1 -> WDS. First entry must start as follows:
  616. ;                   +-----------------+
  617. ;                   | Destination addr|
  618. ;                   +-----------------+
  619. ;                   |                 |     [ for source addr ]
  620. ;                   +-----------------+
  621. ;                   |  LAP type code  |
  622. ;                   +-----------------+
  623. ;                   :                 :
  624. ;       A2 -> local variables
  625. ;
  626. ;   Return:
  627. ;       D0 = error code
  628. ;
  629. ; NOTE: for MPP, first two data bytes must be length
  630. ;____________________________________________________
  631.  
  632. MWriteLAP   MOVE.L  2(A1),A0            ; A0 -> first WDS entry
  633.             MOVEQ   #LAPProtErr,D0      ; Assume an error (2.3F)
  634.             TST.B   LAPType(A0)         ; Make sure protocol is a valid one
  635.             ble.s   MWRLAPex            ; Return error if not
  636.             MOVE.B  LAPDstAdr(A0),D2    ; D2 = destination address
  637.             bsr.s   LAPWrite            ; Write out the packet
  638. MWRLAPex    bra     AbusExit
  639.             EJECT
  640. ;___________________________________________________________________________
  641. ;
  642. ; LAPWrite - send a packet out an Async port.  Called both by MWriteLAP
  643. ; and DDPWrite.
  644. ;
  645. ;   Call:
  646. ;       A1 -> WDS (first entry must start as in MWriteLAP above)
  647. ;       A2 -> local variables
  648. ;       D2 = LAP destination address
  649. ;
  650. ;   Return:
  651. ;       D0 = noErr or the error code
  652. ;       Uses D1-D3,A0,A1,A3
  653. ;
  654. ; Save the WDS passed in
  655. ; If AALAP isn't up, return noAnswer
  656. ; Next, check the length of the frame for <= 603 bytes; return error if bad
  657. ; If we're currently sending a frame:
  658. ; if it's an IM/UR, simply return (WDS will be sent when done)
  659. ; if it's not, then stop (somehow we got two frames to send from DevMgr)
  660. ; If interrupts are on 
  661. ;    Update PollProc pointer if it needs it
  662. ;    Check that the AALAP is still working, sending IM/UR if necessary.
  663. ; Start sending the frame
  664. ; This code relies on the Device Manager for queuing.  Here's how it works:
  665. ;
  666. ; General Rule #1: All operations initiated by the device manager 
  667. ; ultimately return to the DevMgr through jIOdone.
  668. ;
  669. ; General Rule #2:  All async operations which cannot complete immediately 
  670. ; return thru a RTS.  When the operation does complete, the (interrupt)
  671. ; routine can go thru jIOdone.
  672. ;
  673. ; Specific AppleTalk Rule #1:  All callers of LAPWrite have bra AbusExit
  674. ; code right after the call to LAPWrite.  This eventually jumps to jIOdone.
  675. ;
  676. ; Specific AppleTalk Rule #2:  Since they've taken care of the details,
  677. ; LAPWrite only has to remember two things: If we finish, we can return
  678. ; to our original caller (by jumping thru LAPWrtRtn to go to the device 
  679. ; manager);  If we don't finish, we should return to the caller's caller
  680. ; (which called the device manager in the first place).  Whew!
  681. ;___________________________________________________________________________
  682.  
  683. LAPWrite    move.l    (SP)+,LAPWrtRtn(A2) ; save the caller's adrs
  684.             move.l    A1,WDSptr(A2)       ; and the frame we're asked to send
  685.  
  686.             move.w    #noAnswer,D0
  687.             tst.b     AALAPup(a2)         ; is the AALAP up?
  688.             beq       LAPWexit            ; exit if bad
  689. ;
  690. ; Next compute the length of the WDS -- exit if it's bad
  691. ;
  692.             move.l  A1,A0                ; get the WDS pointer
  693.  
  694.             clr.l   D2                  ; D2 = number of data bytes in frame
  695.             clr.l   D1                  ; D1 = number of segments in WDS
  696.             cmp.w   #2,(a0)             ; is first segment too short?
  697.             ble.s   LAPWexit            ; go if it is
  698. @20         tst.w   (a0)                ; is WDS length = 0?
  699.             beq.s   @30                 ; go if so
  700.             add.w   (a0),d2             ; add in this length
  701.             addq    #1,d1               ; incr the segment counter
  702.             addq.l  #6,A0               ; bump the WDS pointer
  703.             bra.s   @20
  704.  
  705. ; D2 is the length of the message we've been asked to send
  706. ; D1 is the number of segments we've been presented with
  707. ; (A1 still has WDSptr)
  708.  
  709. @30         moveq   #LAPProtErr,D0
  710.             tst.l   d1                  ; is D1 (number of segments) < 1?
  711.             ble.s   LAPWexit            ; go if so (error)
  712.             moveq   #ddpLenErr,D0 
  713.             cmp.w   #603,D2             ; is the length > 603 (3 LAP + 600
  714. data)
  715.             bgt.s   LAPWexit            ; go if it's bad
  716. ;
  717. ; we can try to send WDS in A1 -- are we currently sending a frame?
  718. ;
  719.             tst.l   tWDSptr(a2)         ; are we presently sending a frame?
  720.             beq.s   @40                 ; go if not
  721.             tst.b   SendingIMUR(A2)     ; is it an IM or UR?
  722.             beq.s   @35                 ; go if not
  723.             tst.l   qWDSptr(A2)         ; is one already queued?
  724.             bne.s   @35                 ; go if so (stop)
  725.             move.l  A1,qWDSptr(A2)      ; save the (queued) WDS pointer
  726.            _statcount DeferXmit
  727.             rts   
  728. @35         pea     AALAP2in1           ; point at the string
  729.             DC.W    $ABFF               ; and trap 'em (in lieu of $A9FF)
  730.  
  731. ;
  732. ; WDS in A1 is OK to send now: if interrupts enabled, 
  733. ;    update PollProc and check time since last good frame
  734. ;
  735. @40         move    SR,D0
  736.             and     #$70,D0             ; is the interrupt mask <> 0?
  737.             bne.s   SendWDSptr          ; just send it 
  738. ; Update our local PollProc pointer
  739. ;
  740.             move    SR,-(A7)            ; save the state
  741.             move    #SCCLockout,SR      ; turn off interrupts
  742.             lea     myPollProc,A1       ; A1 -> our PollProc
  743.             move.l  PollProc,D0         ; get the current PollProc address
  744.             cmp.l   D0,A1               ; have we already updated it?
  745.             beq.s   @50                 ; go if we have
  746.             move.l  D0,SavePS(A2)       ; else update our saved copy
  747.             move.l  A1,PollProc         ; and point the real PollProc at us
  748. @50         move    (A7)+,SR            ; and re-enable
  749. ;
  750. ; check for (Ticks - LastRcv) > 1800 - see if they're still there
  751. ;
  752.             move.l  Ticks,D0            ; have we received a frame recently?
  753.             sub.l   LastRcv(a2),D0
  754.             cmp.l   #1800,D0            ; (ticks - LastRcv) > 1800 (30 sec)?
  755.             bmi.s   SendWDSptr          ; go if not (send it)
  756.             bsr     Get_NNNN            ; do the IM/UR stuff
  757.             beq.s   SendWDSptr          ; go if it worked
  758.             move.w  D0,-(SP)            ; otherwise, save the status
  759.             bsr     DoWarn              ; else, warn them
  760.             move.w  (SP)+,D0            ; and return bad status
  761. ;
  762. ; Come here if we need to return immediately (status is in D0)
  763. ;
  764. LAPWexit    move.l   LAPWrtRtn(A2),A0   ; this'll get 'em to IOdone
  765.             jmp      (A0)               ; sooner or later
  766.  
  767. AALAP2in1    dc.b 24
  768.             dc.b     'AALAP - TWO MSGS AT ONCE'
  769.             align 2
  770.             EJECT
  771. ;____________________________________________________________________
  772. ;
  773. ; SendFrame -- Starts off transmission of a frame
  774. ;
  775. ; A0 points to the WDS of the frame to send
  776. ;
  777. ; SendFrame sets all the pointers, etc.  and then sends the FrameChar 
  778. ; ($A5).  The Transmit Interrupt Handler ships all the remaining bytes
  779. ; as they are needed.
  780. ;
  781. ;____________________________________________________________________
  782.  
  783. SendWDSptr  move.l  WDSptr(A2),A0       ; get the WDS to send
  784. SendFrame   move.w  (a0)+,D0            ; D0 = the length of the 1st segment
  785.             move.l  (a0)+,a1            ; a1 -> the first byte of 1st segment
  786.             move.l  a0,tWDSPtr(a2)      ; and save the pointer to rest of WDS
  787.             subq    #2,D0               ; Finagle the length and address
  788.             move    D0,TxCount(a2)      ;   of the segment (AALAP doesn't
  789.             addq.l  #2,A1               ;   send dest and source node)
  790.             move.l  a1,LAPFetch(a2)     ;
  791.             st      nCRC(a2)            ; we'll need to send a CRC
  792.             st      nFrmChr(a2)         ;    and a closing FrameChar
  793.             sf      EscOut(a2)          ; clear the Escape flag
  794.             clr     OutputCRC(a2)       ; and the CRC
  795.             moveq   #qFrmChar,D0        ; load a FrameChar
  796.             bra.s   SendSCC             ; and kick off the frame
  797.            EJECT
  798. ;____________________________________________________________________
  799. ;
  800. ; LAPSend -- send the next byte in the LAP frame
  801. ;
  802. ; This routine checks to see if we're flow-controlled, if not, it
  803. ; gets the next char, accumulates the CRC, generates DLE's as 
  804. ; required, and calls the routine to place the byte in the SCC.
  805. ;
  806. ; It works from LAPFetch(a2), and advances it (and decrements TxCount)
  807. ; as necessary.
  808. ;
  809. ; If we sent a char, then we set SentChar(A2) to true
  810. ;____________________________________________________________________
  811.  
  812. LAPSend     tst.b   RcvdXoff(a2)        ; are we flow controlled?
  813.             bne.s   LAPSendRTS          ; go if so
  814.  
  815.             move    TxCount(a2),D3      ; get the remaining length
  816.             ble.s   LAPBadCount         ; go if zero or negative
  817.             cmp.w   #maxLAPFrmLen,D3    ; check its length
  818.             bgt.s   LAPBadCount         ; go if too big
  819.  
  820.             move.l  Ticks,LastXmit(a2)  ; remember when we last sent a char
  821.             subq    #1,D3               ; decr the count
  822.             move.l  LAPFetch(a2),a0     ;
  823.             move.b  (a0)+,D0            ; and fetch the character, bumping the ptr
  824.  
  825.             tst.b   EscOut(a2)          ; are we escaping this char?
  826.             bne.s   @15                 ; go if yes -- it's already in CRC
  827.             lea     OutputCRC(a2),a3    ; point at the output CRC Accumulator
  828.             bsr     NextCRC             ; accumulate the un-processed char
  829.  
  830.             cmp.b   #DLE,D0             ; test for DLE, Xon, Xoff, FrameChar
  831.             beq.s   @10                 ; go if it's a special one
  832.             cmp.b   #FrameChar,D0
  833.             beq.s   @10
  834.             move.b  D0,D1
  835.             and.b   #$7F,D1             ; is it a XON or XOFF (either parity)?
  836.             cmp.b   #Xoff,D1
  837.             beq.s   @10
  838.             cmp.b   #Xon,D1
  839.             bne.s   @20                 ; go if it's just a normal character
  840.  
  841. @10         st      EscOut(a2)          ; remember that we're escaping
  842.             moveq   #DLE,D0             ; data to send is a DLE
  843.             bra.s   SendSCC             ;  (and don't update the pointer/len)
  844.  
  845. @15         eor     #$40,D0             ; come here if we're escaping this char
  846. @20         move.l  a0,LAPFetch(a2)     ; update the pointer
  847.             move    D3,TxCount(a2)      ;    and the remaining length
  848.             sf      EscOut(a2)          ;
  849. ;                                       ; D0 has the next char to send
  850. ;           bra.s   SendSCC             ; and send the character
  851. ;
  852. ; SendSCC -- sends D0 to the SCC Write Data Register
  853. ;            Assumes that SCC is ready (TBMT is true)
  854. ; Returns D0 = 0
  855. ; uses A1
  856. ;
  857. SendSCC     st      SentChar(A2)        ; remember we sent a char
  858.             move.l  SCCWr,a1            ; point at the SCC Write Control
  859.             IF      PortA THEN
  860.             addq.l  #ACtl,a1            ; add in the offset for Port A
  861.             ENDIF
  862.             move.b  D0,SCCData(a1)      ; output the character
  863.             moveq   #0,D0               ; clear the return status
  864. LAPSendRTS  rts                         ; and return
  865. LAPBadCount pea     BadCntStr
  866.             DC.W    $ABFF               ; Trap 'em (not $A9FF)
  867.             rts
  868. BadCntStr    DC.B       10
  869.             DC.B    'Bad length'
  870.             align 2
  871.             EJECT
  872. ;____________________________________________________________________
  873. ;
  874. ; SendChar -- Synchronously wait for TBMT and send another character
  875. ;       Use Ticks to watch for 1/2 sec timeout, so we don't hang forever
  876. ;
  877. ; Entry:        D0 = char to send
  878. ; Exit:         D0 = 0000 if OK
  879. ;               D0 = BadTBMT if we timed out (-3110)
  880. ;               A0,A1,D2 changed
  881. ;____________________________________________________________________
  882.  
  883. SendChar    _SUBR
  884.             move    Ticks,D2            ; fail-safe counter
  885.             add.l   #30,D2              ; bump by 1/2 second
  886. @10         bsr.s   TestTBMT            ; look to see if we can send it
  887.             bne.s   @20                 ; go if we can 
  888.             cmp.l   Ticks,D2            ; did we time out?
  889.             bpl.s   @10                 ; go if not
  890.             move    #-3110,D0           ; BadTBMT return code
  891.             bra.s   @40
  892.  
  893. @20         bsr.s   SendSCC              ; else send it
  894.  
  895. @40         _SUBEND 'SENDCHAR'
  896. ;
  897. ; Check state of TBMT - sets CCR to state of TBMT
  898. ; Uses A0
  899. ;
  900. TestTBMT     movem.l SCCRd,A0            ; point at the SCC
  901.             IF      PortA THEN
  902.             addq.l  #Actl,A0
  903.             ENDIF
  904.             btst    #TxEmptyBit,(a0)     ; is the TBMT set?
  905.             rts                          ; return
  906.             EJECT
  907. ;____________________________________________________________________
  908. ;
  909. ; TIntHnd -- this code catches the Tx Buffer Empty interrupts from
  910. ;      the SCC and tries to send another character.  If it could not
  911. ;      send a character, it clears the Tx Pending bit, so that the SCC
  912. ;      will not interrupt again.  Finally (in any case) it also resets 
  913. ;      the highest interrupt under service (IUS) in the SCC to clear
  914. ;      the interrupt before returning.
  915. ;
  916. ; On entry, A0/A1 point to the SCC control read/write registers.
  917. ; Like a normal interrupt handler, it must preserve D4-D7 and A4-A7
  918. ;____________________________________________________________________
  919.  
  920. TIntHnd     move.l  MPPVars,a2          ; point at the MPP Variables
  921.             _statcount XmitCount        ;
  922.  
  923.             sf      SentChar(A2)        ; 
  924.             bsr.s   TxNextCh            ; try to send another char
  925.  
  926.             tst.b   SentChar(A2)        ; did we?
  927.             bne.s   TintIUS             ; go if so
  928.             move.l  SCCWr,A1            ; otherwise reset TxPend 
  929.             IF      PortA THEN
  930.             addq.l  #Actl,A1
  931.             ENDIF
  932.             move.b  #$28,(A1) 
  933. TIntIUS     bra     DoIUS               ; and reset the highest IUS
  934.  
  935. ;___________________________________________________________________________
  936. ;
  937. ; TxNextCh -- try to send (in this order)
  938. ;          the next character of the segment, or 
  939. ;          the next segment, or 
  940. ;          the CRC, or 
  941. ;          the trailing FrameChar.
  942. ;
  943. ; If a complete frame which was initiated by the device manager has 
  944. ; been sent, we should jump thru IODone (asking the DevMgr for more 
  945. ; to do).  Otherwise, (it was an IM or UR) we look to see if there 
  946. ; is a frame from the DevMgr queued (in WDSptr).  If so, we start 
  947. ; sending it, otherwise, we simply RTS.
  948. ;___________________________________________________________________________
  949.  
  950. TxNextCh    move.l  tWDSPtr(a2),D0      ; D0 -> WDS in progress
  951.             beq.s   TxNextRTS           ; if nil, just exit (no message)
  952.             tst.w   TxCount(A2)         ; is there more of the segment to send
  953.             bne     LAPSend             ; if so, send next character
  954.             
  955. @5          move.l  D0,A0               ; otherwise, point at the WDS
  956.             tst     (a0)                ; check the next length
  957.             beq.s   @10                 ; go if it's zero (end of the frame)
  958.             move    (a0)+,TxCount(a2)   ; otherwise, update TxCount and
  959.             move.l  (a0)+,LAPFetch(a2)  ;    and LAPFetch
  960.             move.l  a0,tWDSptr(a2)      ; and update the tWDSPtr
  961.             bra     LAPSend             ; and send it off
  962. ;
  963. ; Now send the CRC
  964. ;
  965. @10         tst.b   nCRC(a2)            ; do we need to send a CRC?
  966.             beq.s   @20                 ; go if not
  967.             sf      nCRC(a2)            ; don't need one now
  968.             move    outputCRC(a2),D0    ; get the two CRC bytes
  969.             ror.w   #8,D0               ; swap them
  970.             lea     CRCBuf(a2),a0       ; point at the CRC Tx Buffer
  971.             move    D0,(a0)             ; save the CRC bytes
  972.             move.l  a0,LAPFetch(a2)     ; and save the fetch pointer
  973.             move    #2,TxCount(a2)      ; save the length, too
  974.             bra     LAPSend             ; and send them off
  975. ;
  976. ; We've sent the CRC, now send the closing FrameChar
  977. ;
  978. @20         tst.b   nFrmChr(a2)         ; do we need to send a FrameChar?
  979.             beq.s   @30                 ; go if not
  980.             sf      nFrmChr(a2)         ;
  981.             moveq   #qFrmChar,D0        ; get $A5
  982.             bra     SendSCC             ; send it and exit
  983. ;
  984. ; We've sent a full frame, now clean up
  985. ;
  986. @30         clr.w   TxCount(a2)         ; clear the TxCount
  987.             clr.l   tWDSPtr(a2)         ; clear the tWDSptr (no longer sending)
  988. ;
  989. ; Now decide whether to return, wakeup the Dev. Mgr, or start a queued frame
  990. ;
  991.             tst.b   SendingIMUR(A2)     ; were we sending an IM or UR?
  992.             beq.s   NotIMUR             ; go if not
  993.             sf      SendingIMUR(A2)     ; well, we're not anymore
  994.             move.l  qWDSptr(A2),D0      ; is there a queued frame?
  995.             beq.s   TxNextRTS           ; go if not
  996.             move.l  D0,A0
  997.             bra     SendFrame           ; otherwise, start sending it
  998. TxNextRTS   rts                         ; otherwise, return (RTS)
  999. ;
  1000. ; We weren't sending IM/UR so we must have finished a msg from the 
  1001. ; device mgr.  Therefore, we should return to the Device Manager.
  1002. ;
  1003. NotIMUR     clr.l   qWDSptr(A2)         ; clear out the WDS
  1004.             moveq   #0,D0               ; good return status
  1005.             bra     LAPWexit            ; and go thru LAPWrtRtn to IOdone
  1006.             EJECT
  1007. ;___________________________________________________________________________
  1008. ;
  1009. ; RandomWord - generate a random number
  1010. ;
  1011. ;   Call:
  1012. ;       RandomSeed(A2) = seed
  1013. ;
  1014. ;   Return:
  1015. ;       D0 = random number (CCR set to it)
  1016. ;___________________________________________________________________________
  1017.  
  1018. RandomWord  MOVE    RandomSeed(A2),D0   ; D0 = current seed
  1019.             MULU    #773,D0             ; Times 773
  1020.             ADDQ    #1,D0               ; Plus 1
  1021.             MOVE    D0,-(SP)            ; Save high byte on stack
  1022.             LSL     #8,D0               ; Put low byte into high byte
  1023.             MOVE.B  (SP)+,D0            ; And high byte into low byte
  1024.             MOVE    D0,RandomSeed(A2)   ; Set back in seed
  1025.             RTS
  1026.             EJECT
  1027. ;________________________________________________________________________
  1028. ;
  1029. ; VBL handler - come here every VBLtimer ticks. Used to check for long 
  1030. ; output puases; if we stop for > 1 second, we expermientally send
  1031. ; the next character.
  1032. ;   A0 -> VBL queue element
  1033. ;________________________________________________________________________
  1034.  
  1035. VBLHnd      MOVE    #VBLtimer,VBLCount(A0) ; Better re-init VBL count
  1036.             MOVE.L  MPPVars,A2             ; A2 -> local variables
  1037. ;
  1038. ; Have we sent an Xoff (did we set nXon)?  If so, try to send an Xon
  1039. ;
  1040.             tst.b   nXon(A2)               ; do we need an Xon?
  1041.             beq.s   @20                    ; go if not
  1042.             bsr     TestTBMT               ; try to send it to the SCC
  1043.             beq.s   VBLHndRTS              ; quit if we couldn't send it
  1044.             moveq   #Xon,D0  
  1045.             bsr     SendSCC                ; send an Xon
  1046.             sf      nXon(A2)               ; and clear the flag
  1047.             bra.s   VBLHndRTS              ; and quit
  1048. ;
  1049. ; Check for long pause during transmit
  1050. ;
  1051. @20         tst.l   tWDSptr(A2)         ; do we have anything to send?
  1052.             beq.s   VBLHndRTS           ; return if not
  1053.             move.l  Ticks,D0
  1054.             sub.l   LastXmit(a2),D0     ; if (ticks - LastXmit) > 60 then
  1055.             cmp     #60,D0              ; let's try to send another char
  1056.             bmi.s   VBLHndRTS
  1057.             bsr     TestTBMT            ; is TBMT set (can we send another char?)
  1058.             beq.s   VBLHndRTS           ; go if not
  1059.             sf      RcvdXoff(a2)        ;
  1060.             _statcount XOFFTOcount
  1061.             MOVE    #SCCLockout,SR      ; exclude SCC interrupts (VIA priority < SCC)
  1062.             bsr     TxNextCh            ; otherwise, do another character
  1063. VBLHndRTS   rts                         ; this'll restore SR et al
  1064.             eject
  1065. ;___________________________________________________________________________
  1066. ;
  1067. ;   myPollProc -- AALAP PollProc addendum (predendum?):
  1068. ;
  1069. ;   The AALAP needs a bit of a PollProc, since it will lose characters
  1070. ;   whenever the disk spins.  Of course, all good Macintosh programmers
  1071. ;   know that the Printer Port (PortB) isn't polled by the disk driver
  1072. ;   since there's just not enough horsepower to go around.
  1073. ;
  1074. ;   The PollProc is called by the disk driver to poll PortA.  We
  1075. ;   execute a snippet of code before the real PollProc, and send an 
  1076. ;   Xoff to the other end if we're receiving or processing a message 
  1077. ;   while the disk is spinning.  Then we transfer to the real PollProc.
  1078. ;
  1079. ;   This routine preserves all regs except the SR.  It does this by
  1080. ;   reserving a longword on the stack, and then stuffing the SavePS
  1081. ;   value in it.  If it's zero, then there wasn't a PollProc, and we
  1082. ;   pop that value off the stack and return to the disk driver.  If
  1083. ;   that value wasn't zero, then the real PollProc's address will be
  1084. ;   on the top of the stack, and we go there.  The disk driver's return
  1085. ;   address will be left on the stack, allowing the PollProc to return
  1086. ;   normally.
  1087. ;
  1088. ;   InpState and stillBusy must both be in the same word.  The 
  1089. ;   tst.w InpState(A2) below fails otherwise.
  1090. ;___________________________________________________________________________
  1091.  
  1092. myPollProc  subq    #4,A7                 ; save space for a return adrs
  1093.             move.l  A2,-(SP)              ; and save A2 
  1094.             move.l  MPPVars,A2            ; point at the MPP locals
  1095.             tst.b   nXon(A2)              ; have we already sent an Xoff?
  1096.             bne.s   myPPexit              ; go if so
  1097.             tst.w   InpState(A2)          ; are we receiving or processing a message?
  1098.             beq.s   myPPexit              ; go if not
  1099.  
  1100.             movem.l A0/A1/D0,-(SP)        ; save regs 
  1101. @10         bsr     StashSCCch            ; grab a char from the SCC, save it
  1102.             bne.s   @10                   ; loop 'til it's empty
  1103.             statcount PPCount
  1104.             bsr     TestTBMT              ; is it OK to send the Xoff?
  1105.             beq.s   @30                   ; go if not
  1106.             moveq   #Xoff,D0
  1107.             bsr     SendSCC               ; send Xoff
  1108.             st      nXon(A2)              ; and remember we need Xon
  1109.             statcount PPXoffCnt
  1110. @30         movem.l  (SP)+,A0/A1/D0       ; restore the regs
  1111.  
  1112. myPPexit    move.l   SavePS(A2),4(SP)     ; move address onto stack (sets CC)
  1113.             movea.l (SP)+,A2              ; restore A2
  1114.             bne.s    @20                  ; go if PollProc adrs <> 0 (use it)
  1115.             addq.l   #4,SP                ; else pop the (nil) adrs
  1116. @20         rts                            ; and go there
  1117.  
  1118.             EJECT
  1119. ;___________________________________________________________________________
  1120. ;
  1121. ;  ExtIntHnd -- catch the External or Status Interrupts from the SCC
  1122. ;
  1123. ;  Checks for mouse interrupt, passes control if it is one, else resets
  1124. ;  the external/status SCC interrupts.
  1125. ;___________________________________________________________________________
  1126.  
  1127. ExtIntHnd   btst    #DCDbit,D1          ; did the DCD bit change (mouse moved)
  1128.             beq.s   @10                 ; go if not
  1129.             move.l  MouseVector,A3      ; else, point at the mouse handler
  1130.             jmp     (A3)                ; and go there
  1131.  
  1132. @10         move.b  #$10,(a1)           ; reset ext interrupts
  1133.             move.b  #$10,(a1)           ;    (twice)
  1134.             move.b  #ResetIUS,(a1)      ; Reset Highest IUS in SCC (to WR0)
  1135.             rts
  1136.             EJECT
  1137. ;___________________________________________________________________________
  1138. ;
  1139. ; RIntHnd  - SCC receive interrupt handler
  1140. ;
  1141. ;   Called: A0 -> SCC control read register
  1142. ;           A1 -> SCC control write register
  1143. ;
  1144. ; This code is structured differently from the ABLAP code, since
  1145. ; the arrival rate of the chars is so much slower for AALAP.  Normal
  1146. ; ABLAP routines call ReadPacket and ReadRest to get pieces or the rest
  1147. ; of the frame as they arrive in real time.  With AALAP, the character
  1148. ; arrival rate is so slow that we copy the entire frame into an
  1149. ; interrupt-time buffer.
  1150. ;
  1151. ; When we receive a good frame, we then pass control to the appropriate
  1152. ; protocol handler, which then makes calls on ReadPacket and ReadRest to
  1153. ; dole out the characters as necessary.
  1154. ;
  1155. ; Like all Mac interrupt handlers, it must preserve D4-D7 and A4-A7.
  1156. ; and return with a RTS instruction.
  1157. ;
  1158. ; Since the default DDP socket listener is quite slow (3-4 msec to process
  1159. ; a newly received message) we set up a buffer to contain characters
  1160. ; which arrive during the time the socket listener is in control.  We
  1161. ; set a flag (stillBusy) to indicate that we're still busy, and save the
  1162. ; chars in BusyBuf.
  1163. ;_________________________________________________________________________
  1164.  
  1165. SpIntHnd
  1166. RIntHnd     move.l  MPPVars,A2          ; A2 -> driver variables
  1167.             _statcount RcvIntCount      ; remember the number of Rcv
  1168. interrupts
  1169.  
  1170. RIntHnd10   bsr     NextChar            ; handle next char (from BusyBuf or
  1171. SCC)
  1172.             beq     RIntRTS             ; quit if no data
  1173.  
  1174.             and     #$00FF,D0           ; use only eight bits
  1175.             move.w  D0,LastRxCh(a2)     ; remember the char
  1176. ;
  1177. ; Check for flow control from other side
  1178. ;
  1179. @15         move.b  D0,D1               ; check for either parity Xon/Xoff
  1180.             and.b   #$7F,D1
  1181.             cmp.b   #Xoff,D1            ; is it a control-S?
  1182.             bne.s   @20                 ; go if not
  1183.             _statcount XOFFcount        ; count it
  1184.             st      rcvdXoff(a2)        ; and remember we received Xoff
  1185.             bra.s   RIntHnd10           ; loop for another char
  1186.  
  1187. @20         cmp.b   #Xon,D1             ; or is it a control-Q?
  1188.             bne.s   @30                 ; go if not
  1189.             _statcount XONcount
  1190.             sf      rcvdXoff(a2)
  1191.             bsr     TestTBMT            ; is the tx empty?
  1192.             beq.s   RIntHnd10           ; loop if not
  1193.             bsr     TxNextCh            ; otherwise, start up Tx side again
  1194.             bra.s   RIntHnd10           ; loop for another char
  1195. ;
  1196. ; Watch out for framing characters
  1197. ;
  1198. @30         cmp.b   #FrameChar,D0       ; is it a framing character?
  1199.             beq.s   GotFrmCh            ;    go if so
  1200.             tst.b   InpState(a2)        ; are we in a frame?
  1201.             beq.s   RintHnd10           ;    loop for another char
  1202.             EJECT
  1203. ;
  1204. ; Maybe this is a data char -- check the frame length
  1205. ;
  1206.             cmp     #MaxLAPFrmLen,rcvdlen(a2) ; is the frame too long?
  1207.             bls.s   @50                 ; go if it's OK
  1208.             _statcount LongFrame        ; remember the long frame
  1209.             sf      InpState(a2)        ; go idle
  1210.             bra.s   RIntHnd10           ; loop for another char
  1211. ;
  1212. ; We have a real char -- un-escape it
  1213. ;
  1214. @50         cmp.b   #DLE,D0             ; is it a DLE?
  1215.             bne.s   @90                 ; go if not
  1216.             st      EscIn(a2)           ; remember we've seen an escape
  1217.             bra.s   RIntHnd10
  1218. ;
  1219. ; This is a data char -- complete any escaping, accumulate the CRC
  1220. ;
  1221. @90         tst.b   EscIn(a2)           ; should we escape it?
  1222.             beq.s   @100                ; go if not
  1223.             eor     #$40,D0             ; xor with $40
  1224.             sf      EscIn(a2)           ; and clear the escape flag
  1225.  
  1226. ; now we've got a good char
  1227. @100        lea     inputCRC(a2),a3     ; point at the CRC accumulator
  1228.             bsr     NextCRC             ; update the CRC accum using byte in
  1229. D0
  1230.             move.l  LAPStash(a2),a0     ; point at the next free char in
  1231. buffer
  1232.             move.b  D0,(a0)+            ; save the char in the buffer, bump the
  1233. pointer
  1234.             addq    #1,rcvdlen(a2)      ; increment the bytes-read counter
  1235.             cmp     #3,rcvdlen(a2)      ; have we read in exactly three chars?
  1236.             bne.s   @110                ; go if not
  1237.             move.l  LAPInBuf(a2),a0     ; otherwise point at the LAPInBuf
  1238. @110        move.l  a0,LAPStash(a2)     ; and update the pointer
  1239.             bra     RIntHnd10           ; loop for another char
  1240.  
  1241. RIntRTS     bra     DoIUS               ; reset Highest IUS and return
  1242.  
  1243. ;
  1244. ; We've discovered a FrameChar -- check if we're done or just starting
  1245. ;
  1246. GotFrmCh    tst.b   InpState(a2)        ; are we in a frame?
  1247.             beq.s   FrmStart            ;  go if not (we will be)
  1248.  
  1249. FrmEnd      cmp     #2,rcvdlen(a2)      ; found closing char
  1250.             bhi.s   CheckCRC            ;    go if frame is long enough
  1251.             _statcount ShortFrame       ; else, flag that we got a short frame
  1252.                                         ;    and fall into FrameStrt
  1253. ;
  1254. ; We're in a frame now!
  1255. ;
  1256. FrmStart    lea     toRHA(a2),a3        ; a3 -> RHA (holds 1st 5 bytes)
  1257.             move.b  sysLAPAddr(a2),(a3)+ ; copy the node number
  1258.             move.b  sysABridge(a2),(a3)+ ; and the bridge address
  1259.             move.l  a3,LAPStash(a2)     ; remember where next byte goes
  1260.             st      InpState(a2)        ; change the InpState to in_msg
  1261.             sf      EscIn(a2)           ; and we're not escaping data
  1262.             clr     InputCRC(a2)        ; no CRC yet
  1263.             clr     rcvdlen(a2)         ; no data, either
  1264.             bra.s   RIntRTS
  1265.             EJECT
  1266. ;
  1267. ; We received a complete frame -- check the CRC
  1268. ;
  1269. CheckCRC
  1270.             sf      InpState(a2)        ; we're not in a frame now
  1271.             tst     InputCRC(a2)        ; is the CRC zero?
  1272.             beq.s   LAPDemux            ; go if it is OK
  1273.             _statcount CRCCount         ; save the statistic
  1274.             bra.s   RIntRTS             ; and exit
  1275. ;
  1276. ; Come here on receipt of a good frame.  We've cleared the InpState
  1277. ; to indicate we're out of a frame.
  1278. ;
  1279.  
  1280. LAPDemux    _statcount FrmCount         ; log another good frame
  1281.             move.l  Ticks,LastRcv(a2)   ; remember this frame's arrival time
  1282.             lea     2+toRHA(a2),a3      ; a3 -> LAP type byte
  1283.             MOVE.B  (A3)+,D0            ; Get the LAPtype, bump pointer
  1284.             tst.b   D0
  1285.             BMI     LAPIn               ; If minus, it's a LAP packet
  1286.  
  1287. ;
  1288. ; Got a data packet - look for a protocol handler
  1289. ;
  1290.             tst.b   AALAPup(a2)         ; but first, is the AALAP up?
  1291.             beq.s   @60                 ; go if it's not up
  1292.             MOVEQ   #(LAPTblSz-1),D2    ; D2 = index into active protocols list
  1293. @30         CMP.B   Protocols(A2,D2),D0 ; Match?
  1294.             DBEQ    D2,@30              ; (If none, D2 is negative - 3.1F)
  1295.             LSL.W   #2,D2               ; Make D2 a longword index into Handlers
  1296. ;
  1297. ; Got a protocol handler -- Compute the desired length of the message in D1
  1298. ;
  1299.             move.b  (a3)+,D1            ; Get MSByte of the length into D1
  1300.             and     #3,D1               ; mask for two lsbits
  1301.             LSL     #8,D1               ; Move to proper position
  1302.             MOVE.B  (a3)+,D1            ; D1 = total length
  1303.             move    rcvdlen(a2),D0      ; D0 = total chars received (DDP + LAP
  1304. + CRC)
  1305.             subq    #3,D0               ; disregard LAP type and CRC
  1306.             cmp     D1,D0               ; are they equal?
  1307.             beq.s   @40                 ; go if so
  1308.             _statcount LenErrCnt        ; save the stats
  1309.             bra     RIntRTS             ; and exit
  1310.  
  1311. @40         SUBQ    #2,D1               ; Subtract 2 for length bytes
  1312.             move    d1,RcvdLen(a2)      ; and remember the number of unread chars
  1313.             EJECT
  1314. ;___________________________________________________________________________
  1315. ;
  1316. ;       At this point, Handlers(A2,D2) points to the address of the protocol
  1317. ;       handler for this packet's protocol (or D2 is negative if there is
  1318. ;       none -- 3.1F). JMP to it with the following:
  1319. ;
  1320. ;       A0,A1 = SCC read/write addressses
  1321. ;       A2 = ptr to driver locals
  1322. ;       A3 = ptr into the RHA (first 5 bytes loaded)
  1323. ;       A4 will be the address of our read packet routine
  1324. ;       A5 will be saved for handler's usage (until packet's all in or error)
  1325. ;       D1 = length of packet still left to read (from header)
  1326. ;
  1327. ;       The protocol handler must obey the following conventions:
  1328. ;
  1329. ;       1) It must preserve, across the call, A0-A2, A4 and D1
  1330. ;       2) A6 and D4-D7 must be saved and restored if used.
  1331. ;       3) It must JSR to the routine at (A4) or 2(A4) with registers as defined
  1332. ;          there, for the purpose of reading more of the packet and eventually
  1333. ;          resetting the SCC for the next interrupt.
  1334. ;___________________________________________________________________________
  1335.  
  1336.             TST     D2                  ; Is there a protocol handler? (3.1F)
  1337.             BMI.S   @60                 ; Branch if not
  1338.             bsr     DoIUS               ; reset Highest IUS
  1339.             MOVEM.L A4/A5,SaveA45(A2)   ; Save A4 and A5 (may be free time now)
  1340.             move.l  LAPInBuf(a2),a4     ; point at the next char of the msg
  1341.             move.l  A4,LAPStash(a2)     ;    (we can snatch A4 for a few instrs)
  1342.             MOVE.L  Handlers(A2,D2),A5  ; A5 -> protocol handler
  1343.             LEA     ReadPacket,A4       ; A4 -> ReadPacket
  1344.             st      stillBusy(a2)       ; remember we're processing a frame
  1345.             move.w  VSCCEnable(A2),SR   ; re-enable so we can catch more chars (!)
  1346.  
  1347.             JSR     (A5)                ; Call the protocol handler
  1348.  
  1349.             move.l  MPPVars,A2          ; point at our variables
  1350.             cmpa.l  SaveA45(A2),A4      ; paranoia land -- make sure they've left
  1351.             bne.s   @45                 ;    things as they should be
  1352.             cmpa.l  (SaveA45+4)(A2),A5
  1353.             beq.s   @50
  1354. @45         pea     BadA4A5
  1355.             DC.W    $ABFF               ; print the text (in lieu of $A9FF)
  1356. @50         sf      stillBusy(A2)       ; and now we're not in a frame
  1357.             rts                         ; exit the interrupt handler
  1358. ;
  1359. ; No handler, just log the error
  1360. ;
  1361. @60         _StatCount NoHandCnt        ; Count packets without a handler
  1362.             bra     RIntRTS             ; and exit
  1363. BadA4A5     DC.B    17                  ; debugging only
  1364.             DC.B    'AALAP - Bad A4/A5'
  1365.             align 2
  1366.             EJECT
  1367. ;______________________________________________________________________
  1368. ;
  1369. ;   NextChar -- Handle the next char
  1370. ;
  1371. ;   This routine does two things:  If we're awaiting a full message, then
  1372. ;   it gets the next character.  That char may have arrived from the SCC,
  1373. ;   or it may be a char left in the BusyBuf. (Chars in the BusyBuf take
  1374. ;   precedence.)
  1375. ;
  1376. ;   If we're still processing the previous message (stillBusy set true),
  1377. ;   then all characters which arrive will be placed in BusyBuf, and the
  1378. ;   associated pointers updated.  (Note: myPollProc also inserts data
  1379. ;   into the BusyBuf, but it doesn't set stillBusy.)
  1380. ;
  1381. ;   Uses    A0,A1,D0
  1382. ;   Assumes A2 -> MPPVars
  1383. ;
  1384. ;   Returns Z if no character
  1385. ;           NZ if char present (char is in 8 lsbits of D0)
  1386. ;______________________________________________________________________
  1387.  
  1388. NextChar    _SUBR
  1389.             tst.b   stillBusy(A2)       ; are we still processing the prev.
  1390. frame?
  1391.             bne.s   @30                 ; go if we are
  1392.  
  1393.             bsr.s   GetBusyChar         ; else, look for a char from BusyBuf
  1394.             bne.s   @50                 ; quit if we got one
  1395.             bsr.s   GetSCCchar          ; else check the SCC
  1396.             bra.s   @50                 ; and quit
  1397.  
  1398. @30         bsr.s   StashSCCch          ; stash a char from SCC into BusyBuf
  1399.             bne.s   @30                 ; go back and look for more
  1400.  
  1401. @50         _SUBEND 'NEXTCHAR'
  1402.             _assumeEq BusyStash,BusyBuf+16 ; otherwise cmpa.l A0,A1 (above)
  1403. fails
  1404.  
  1405. GetBusyChar _SUBR                       ; get a char from the BusyBuf
  1406.             move.l  BusyFetch(A2),D0    ; get the fetch pointer
  1407.             cmp.l   BusyStash(A2),D0    ; is it the same as the stash pointer
  1408.             bne.s   @10                 ; go if not (more chars to do)
  1409.             lea     BusyBuf(a2),a0      ; point at the busy buffer
  1410.             move.l  a0,BusyStash(A2)    ; and save it in the BusyStash
  1411.             move.l  a0,BusyFetch(A2)    ; and BusyFetch
  1412.             moveq   #0,D0               ; clear the CC
  1413.             bra.s   @20
  1414. @10         move.l  D0,A0               ; there's still more to take
  1415.             move.b  (A0)+,D0            ; get the byte
  1416.             move.l  A0,BusyFetch(A2)    ; update the pointer
  1417.             or.w    #$100,D0            ; make CC <> Z (must preserve 8
  1418. lsbits)
  1419. @20         _SUBEND  'GETBUSYC'
  1420.             EJECT
  1421. ;______________________________________________________________________
  1422. ;
  1423. ; GetSCCchar and StashSCCch both are called by RintHnd and myPollProc
  1424. ; BOTH ROUTINES MAY ONLY USE A0, A1, AND D0!!!!!  (A2 will -> MPPVars)
  1425. ;
  1426. ;   GetSCCchar looks at RCA on the proper channel, and returns the char
  1427. ;   in D0 if there was one (with CC set <> Z); else it returns CC = Z.
  1428. ;______________________________________________________________________
  1429. GetSCCchar  movem.l SCCRd,A0/A1         ; forces A0/A1 to point at SCC
  1430.             IF      PortA THEN
  1431.             addq.l  #Actl,A0
  1432.             addq.l  #Actl,A1
  1433.             ENDIF
  1434.             btst    #RCAbit,(A0)        ; is there a char?
  1435.             beq.s   @20                 ; go if not
  1436.             move.b  #1,(a1)             ; point at the error bits from RR1
  1437.             nop
  1438.             move.b  (a0),D0             ; get them (Overrun,Framing) in D0
  1439.             and     #$70,D0             ; any error bits?
  1440.             beq.s   @10                 ; go if not
  1441.             move.b  #ResetErr,(a1)      ; else send Error Reset to WR0
  1442.             nop
  1443.             move.b  #1,(a1)             ; point at WR1
  1444.             nop
  1445.             move.b  #$13,(a1)           ; and set up for int on all rx chars
  1446.             nop
  1447.             _statcount OVRcount         ; count 'em
  1448. @10         move.b  SCCData(a0),D0      ; and get the data (EVEN IF ERROR!)
  1449.             or.w    #$100,D0            ; set the SR (to NZ -- there's a char)
  1450. @20         rts
  1451. ;
  1452. ;  StashSCCch -- take a char from SCC, save in BusyBuf if there's space
  1453. ;   Return Z if no char or no space; NZ otherwise
  1454. ;
  1455. StashSCCch  bsr.s   GetSCCchar          ; look for a char in the SCC
  1456.             beq.s   @50                 ; go if none
  1457.             lea     BusyStash(a2),A1    ; point at the BusyStash pointer
  1458.             move.l  (a1),A0             ; and get it
  1459.             cmpa.l  A0,A1               ; will this be too many chars?
  1460.             beq.s   @50                 ; yes, simply exit (and ignore the char)
  1461.             move.b  D0,(a0)+            ; save the char, and bump the pointer
  1462.             move.l  A0,BusyStash(A2)    ; and update the pointer
  1463.             or.w    #$100,D0            ; set the CC <> Z ('cause we took one )
  1464. @50         rts                         ; and return
  1465.             EJECT
  1466. ;______________________________________________________________________
  1467. ;
  1468. ; DoIUS  -- reset Highest IUS
  1469. ;______________________________________________________________________
  1470.  
  1471. DoIUS       _SUBR
  1472.             move.l  SCCWr,A1            ; point at the SCC write regs
  1473.             IF      PortA THEN
  1474.             addq.l  #Actl,A1
  1475.             ENDIF
  1476.             move.b  #ResetIUS,(a1)      ; Reset Highest IUS in SCC (to WR0)
  1477.             _SUBEND 'DOIUS   '
  1478.             EJECT
  1479. ;______________________________________________________________________
  1480. ;
  1481. ; LAPIn - it's a LAP control packet.
  1482. ;
  1483. ;   D0 = LAP type
  1484. ;   A3 -> remainder of the frame
  1485. ;   Note:  for IM/UR frames, the net number (2 bytes) is at (a3),
  1486. ;           but the node number (1 byte) is the first byte in LAPInBuf
  1487. ;______________________________________________________________________
  1488. ;
  1489. ; Check for IM
  1490. ;
  1491. LAPIn       move    (a3),D1             ; D1 = Net number (a3 sb even)
  1492.             move.l  LAPInBuf(a2),A0     ; point at first char in input buf
  1493.             move.b  (a0),D2             ; D2 = node number
  1494.             cmp.b   #lapIM,D0           ; is it an IM?
  1495.             bne.s   @60                 ; go if not
  1496.             move    D2,D0               ; D0 = node number
  1497.             sf      RcvdXoff(A2)        ; so we can start sending
  1498.             bsr.s   CheckIM             ; figure out the net and node to send
  1499.             bsr.s   SendIMUR            ; send 'em
  1500.             bra.s   @80
  1501. ;
  1502. ; Check for UR
  1503. ;
  1504. @60         cmp.b   #lapUR,D0           ; is it a UR?
  1505.             bne.s   @80                 ; go if not
  1506.             move    D2,D0               ; D0 = Node number (D1 = Net number)
  1507.             bsr.s   CheckUR             ; check these values, return <> 0 if OK
  1508.             sne     AALAPup(a2)         ; if non-zero, then we're up
  1509. @80         rts                         ; and return
  1510.  
  1511.             _AssumeEq   lapENQ,$81      ; (1)
  1512.             _AssumeEq   lapRTS,lapENQ+3 ; (2)
  1513.             _AssumeEq   lapCTS,lapRTS+1 ; (3)
  1514.             EJECT
  1515. ;__________________________________________________________________________
  1516. ;
  1517. ; CheckIM -- check the received IM frame, compute UR response
  1518. ;
  1519. ; Entry:  D0 = their node number
  1520. ;         D1 = their network number
  1521. ; Exit:   D0,D1 = node, net number for the UR
  1522. ;         D2 = qlapUR
  1523. ;         Changes A0,A1,A3, D0-D3
  1524. ;__________________________________________________________________________
  1525.  
  1526. CheckIM     move.l  #0,A0               ; return nil sometimes
  1527.             move.w  SysNetNum(a2),D2    ; D2 = our Net number
  1528.             beq.s   @10                 ; go if so -- check the node numbers
  1529.             move    D2,D1               ; else, use our net number
  1530. @10         move.b  SysLAPAddr(a2),D3   ; D3 = our node number
  1531. @15         tst.b   D0                  ; while (theirnode <> 0)
  1532.             beq.s   @18                 ; &     (theirnode <> mynode)
  1533.             cmp.b   D3,D0               ; have we both chosen the same value?
  1534.             bne.s   @20                 ; go if not -- return their value
  1535. @18         bsr     RandomWord          ; choose a random value
  1536.             and     #$7F,D0             ; mask to 7 bits
  1537.             bra.s   @15                 ; loop to insure they're different
  1538. @20         move.b  D0,sysABridge(a2)   ; remember their node number
  1539.             moveq   #qlapUR,D2          ; D2 = LAP type
  1540.             rts
  1541.  
  1542.             EJECT
  1543. ;__________________________________________________________________________
  1544. ;
  1545. ; CheckUR -- check the received UR frame
  1546. ;
  1547. ; Entry:  D0 = node number
  1548. ;         D1 = network number
  1549. ; Exit:   D0 =  0 if net/node didn't match
  1550. ;            <> 0 if they matched right off
  1551. ;
  1552. ;__________________________________________________________________________
  1553.  
  1554. CheckUR     SUBR
  1555.             cmp     SysNetNum(a2),D1    ; Network numbers match?
  1556.             bne.s   @10                 ; go if not
  1557.             cmp.b   SysLAPAddr(a2),D0   ; Node number match?
  1558.             bne.s   @10                 ; go if not
  1559.             moveq   #-1,D0              ; make D0 non-zero (it's OK)
  1560.             bra.s   CkURRTS             ; and exit
  1561. @10         tst     SysNetNum(a2)       ; is our network number 0000?
  1562.             bne.s   @50                 ; go if not (we cannot resolve this)
  1563.             move    D1,SysNetNum(a2)    ; save their Net/Node suggestions
  1564.             move.b  D0,SysLAPAddr(a2)
  1565.             bra.s   @60
  1566.  
  1567. @50         st      AALAPstuck(a2)      ; we're really bad off -- NNNN conflict
  1568. @60         clr     D0                  ; we didn't match
  1569.  
  1570. CkURRTS     _SUBEND 'CHECKUR '
  1571.             EJECT
  1572. ;_____________________________________________________________________
  1573. ;
  1574. ; SendIMUR - This routine fills and sends an IM or UR frame.  This is
  1575. ;   a bit dicey, since a UR may be required as a result of receiving
  1576. ;   an IM.  Since it's difficult to abort a frame already in progress,
  1577. ;   we finesse the problem by not sending the IM/UR frame.  Here's why
  1578. ;   it works:  
  1579. ;
  1580. ;   A UR response is only necessary in two cases:  
  1581. ;     a) we're trying to bring the link up, and the other guy said "IM";
  1582. ;     b) he hasn't heard from us, and he wants to make sure we're here.
  1583. ;
  1584. ;   For a), we shouldn't be talking, but he'll ask again anyway;
  1585. ;   for b), the IM is trying to force us to send a good frame.
  1586. ;           If the frame in transit makes it, OK.  If not, he'll
  1587. ;           still ask again.
  1588. ;
  1589. ; Entry:    A0 -> master pointer of this hdlblk
  1590. ;           A2 -> MPPVars
  1591. ;           D0 = node number
  1592. ;           D1 = Net number
  1593. ;           D2 = LAP type
  1594. ; Exit:     A0,A1,A3,D0-D3 changed
  1595. ;_____________________________________________________________________
  1596.  
  1597. SendIMUR    _SUBR
  1598.             tst.l   tWDSptr(A2)         ; are we sending?
  1599.             bne.s   SndIMUR1            ; yes, just return
  1600.             lea     IMURbuf+1(A2),A1    ; A1 points at IMURbuf (odd adrs)
  1601.             move.b  D2,2(a1)            ; save the LAPtype (IM or UR)
  1602.             move.w  D1,3(a1)            ; and the Net number
  1603.             move.b  D0,5(a1)            ; and the Node number
  1604.  
  1605.             lea     IMURwds(A2),A0      ; A0 points at the WDS
  1606.             move.w  #6,(A0)             ; save the length
  1607.             move.l  A1,2(A0)            ; and the pointer to the data
  1608.             clr.w   6(A0)               ; 
  1609.  
  1610.             st      SendingIMUR(A2)     ; remember this!
  1611.             bsr     SendFrame           ; and send it
  1612. SndIMUR1    _SUBEND 'SENDIMUR'
  1613.             EJECT
  1614. ;___________________________________________________________________________
  1615. ;
  1616. ;   ReadPacket - read in the specified number of bytes into the specified
  1617. ;       buffer. It is an error to request more bytes than have been received.
  1618. ;
  1619. ;   ReadRest - read in the rest of the packet, putting the specified number
  1620. ;       of bytes into the specified buffer.  Error if packet longer than buffer.
  1621. ;
  1622. ;   Call:
  1623. ;       A0,A1,A2 = SCC read and write addresses and local variables
  1624. ;       A3 -> buffer to read into
  1625. ;       A4 -> start of ReadPacket
  1626. ;       D3 =  byte count to read (word)
  1627. ;
  1628. ;   Return:
  1629. ;       D0 changed
  1630. ;       D1 number of chars still unread (ReadPacket); modified (ReadRest)
  1631. ;       D2 saved
  1632. ;       D3  = 0 if exact number of bytes requested were read
  1633. ;           > 0 indicates number of bytes requested but not read
  1634. ;               (packet smaller than requested maximum)
  1635. ;           < 0 indicates number of extra bytes read but not returned
  1636. ;               (packet larger than requested maximum)
  1637. ;       A0,A1 preserved by ReadPacket, modified by ReadRest
  1638. ;       A3 -> one past where last character went
  1639. ;       A4,A5 saved (until packet's all in or error)
  1640. ;
  1641. ; NOTE: CRC bytes not included in counts
  1642. ;___________________________________________________________________________
  1643.  
  1644. ReadPacket  BRA.S   DoRP                ; Need this for two entry points
  1645.  
  1646. ReadRest    movem.l a0/a1/D2,-(sp)      ; save some regs
  1647.             move    RcvdLen(a2),D1      ; get the number of remaining chars in D1
  1648.             move    D1,D0               ; we expect to copy D1 bytes
  1649.             move    #0,-(sp)            ; and expect good return status
  1650.             sub     D1,D3               ; compute (D3 - D1)
  1651.             bpl.s   @1                  ; go if we should copy D1 bytes (it fits)
  1652.             add     D3,D0               ; otherwise, copy D3 bytes (d1 + (d3-d1))
  1653.             move    #-1,(sp)            ; and set error return status
  1654. @1          movem.l SaveA45(a2),a4/a5   ; restore A4 and A5
  1655.             bra.s   DoCopy              ; and go to the common code
  1656.  
  1657. DoRP        movem.l a0/a1/D2,-(sp)      ; push some regs
  1658.             move    RcvdLen(a2),D1      ; get the number of remaining chars in D1
  1659.             move    D1,D0               ; assume we'll copy them all
  1660.             move    #-1,-(sp)           ; and that there's an error
  1661.             sub     D3,D1               ; update D1 (remaining bytes in buf)
  1662.             bmi.s   DoCopy              ; go if it's negative (error)
  1663.             move    D3,D0               ; we'll read what they asked for (D3)
  1664.             clr     (sp)                ;  and remember that it's exactly right
  1665.             clr     D3                  ;
  1666.  
  1667. DoCopy      move.l  LAPStash(A2),a0     ; point at the source data
  1668.             ext.l   D0                  ; belt and suspenders (D0 = actual length)
  1669.             add.l   D0,LAPStash(A2)     ;    and update the LAPStash value
  1670.             sub     D0,RcvdLen(a2)      ;    and the num chars remaining
  1671.             move.l  A3,A1               ; point at the dest buffer
  1672.             lea     0(A3,D0),A3         ; update the return pointer
  1673.             _BlockMove                  ; Do It
  1674.             move    RcvdLen(a2),D1      ; return number of unread chars
  1675.             move    (sp)+,d0            ; get the return status back
  1676.             movem.l (sp)+,a0/a1/D2      ; get the other registers
  1677.             tst      D0                 ; set the CCR
  1678.             rts
  1679.             EJECT
  1680. ;___________________________________________________________________________
  1681. ;
  1682. ; NextCRC -- compute a CRC on the word pointed at by A3 and the char in D0
  1683. ;
  1684. ;      This routine computes a CRC-16 on a stream of bytes.  It uses a
  1685. ;      table lookup scheme to implement a x^16 + x^15 + x^2 + 1 polynomial.
  1686. ;      The interested reader is referred to McNamara's Technical Aspects
  1687. ;      of Data Communications, second edition, pps 110-122 for an obliquely
  1688. ;      related discussion.
  1689. ;
  1690. ;      This routine takes the storage short cut of looking up two four-bit
  1691. ;      values in a 16-entry table instead of one eight-bit value in a 256
  1692. ;      word table.  This saves a considerable amount of space (32 bytes vs.
  1693. ;      512 bytes for the table).
  1694. ;
  1695. ;      One pass thru this routine (one character) is about 262 cycles, or
  1696. ;      33.45 usec on a Mac.  This is a data rate of ~29,900 char/sec,
  1697. ;      or plenty fast to keep up with a 9600 baud link.
  1698. ;
  1699. ; Entry:  A3 -> CRC accumulator
  1700. ;         D0    LSbyte is the data char (already masked to 8 bits)
  1701. ;
  1702. ; Exit:   D1,D2 changed
  1703. ;         Other regs unchanged
  1704. ;____________________________________________________________________________
  1705.  
  1706. NextCRC _SUBR      0               ; for macsbug
  1707.        move        (a3),D2         ; D2 is the temp accumulator
  1708.        move        D0,D1           ; make a copy of the input character
  1709. ; first work on the least significant nibble
  1710.        eor         D2,D1           ; xor the accumulator with the data char
  1711.        and         #$0F,D1         ;    to get an index into the CRCTable
  1712.        add         D1,D1           ; to make a word index
  1713.        lsr         #4,D2           ; shift the CRC right four bits
  1714.        move        CRCTable(D1),D1
  1715.        eor         D1,D2           ; and mask it with the approp. table entry
  1716.        move        D0,D1
  1717.        lsr         #4,D1           ; shift the data char right four bits
  1718. ;  and do it again for the high nibble
  1719.        eor         D2,D1           ; xor the accumulator with the data char
  1720.        and         #$0F,D1         ;    to get an index into the CRCTable
  1721.        add         D1,D1           ; to make a word index
  1722.        lsr         #4,D2           ; shift the CRC right four bits
  1723.        move        CRCTable(D1),D1 ; and mask it with the approp. table entry
  1724.        eor         D1,D2
  1725.        move        D2,(a3)         ; remember this CRC for next time
  1726.        _SUBEND     'NEXTCRC '
  1727.  
  1728. CRCTable DC.W      $0000,$CC01,$D801,$1400
  1729.        DC.W        $F001,$3C00,$2800,$E401
  1730.        DC.W        $A001,$6C00,$7800,$B401
  1731.        DC.W        $5000,$9C01,$8801,$4400
  1732.             EJECT
  1733. ;
  1734. *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1735. * * * * * * * ****
  1736. ;___________________________________________________________________________
  1737. ;
  1738. ; MDoWarn -- Control call to put up a warning
  1739. ;
  1740. ; Entry:    A0 -> IOQelement
  1741. ;___________________________________________________________________________
  1742.  
  1743. MDoWarn     move    CSParam(a0),D0      ; get the error code into D0
  1744.             bsr.s   DoWarn              ; warn 'em and return status in D0
  1745.             bra     AbusExit            ; and exit
  1746.  
  1747. ; Put up alerts
  1748. ;____________________________________________________________________
  1749. ;
  1750. ;   DoWarn -- Warn the user...  Give a beep, and display a dialog;
  1751. ;             wait for their choice, then try the NNNN one more time.
  1752. ;             If they choose "Use new address" from Mismatch dialog,
  1753. ;             set SysLAPaddr and SysNetNum to zero before exiting.
  1754. ;
  1755. ;   Entry:   D0 = PortNotCf
  1756. ;                 noAnswer
  1757. ;   Exit:    D0 = -4001 (user clicked OK/Try again)
  1758. ;   -4002  (user clicked Use New)
  1759. ;  -193  (ResFNotFound)
  1760. ;____________________________________________________________________
  1761.  
  1762. resfile     EQU     -2                  ; res file number
  1763. dlgwindow   EQU     -6                  ; dialog window handle
  1764. warning     EQU     -8                  ; error number/return status
  1765. MyCurMap    EQU     -10                 ; save the current res file
  1766.  
  1767. DoWarn      _SUBR   10                  ; Warn the user about troubles
  1768.  
  1769.             move.w  D0,warning(A6)      ; remember the warning
  1770.             move.w  CurMap,MyCurMap(A6) ; and the current res file
  1771.             InitCursor                  ; make it an arrow again
  1772.  
  1773. ; Open our resource file.
  1774.  
  1775.             subq.l  #2,sp               ; make space for result
  1776.             pea     fileName            ; point to file name
  1777.             OpenResFile
  1778.             move.w  (SP)+,resfile(A6)   ; save the resfile number
  1779.             cmp.w   #-1,resfile(a6)     ; check for failure
  1780.             bne.s   @3                  ; branch if ok
  1781.             move.w  #60,-(A7)           ; else beep (long)
  1782.             SysBeep
  1783.             move.w  #ResFNotFound,D0    ; return bad status
  1784.             bra.s   @20                 ; and quit
  1785.  
  1786. ; beep at 'em
  1787. @3
  1788.             move.w  #6,-(A7)            ; 1/10 second beep
  1789.             _SysBeep
  1790.  
  1791. ; choose a dialog to display
  1792.             move.w  #PortNCalrt,D0
  1793.             cmp.w   #PortNotCf,warning(a6) ; which warning?
  1794.             beq.s   @5
  1795.             move.w  #Noansalrt,D0       ; noAnswer dialog
  1796.  
  1797. ; now display the dialog
  1798. @5
  1799.             subq.l  #4,sp               ; space for result of _GetNewDialog
  1800.             move.w  D0,-(sp)            ;    dialog resource ID
  1801.             clr.l   -(sp)               ;    dialog record in heap
  1802.             move.l  #-1,-(sp)           ;    in front of other windows
  1803.             _GetNewDialog
  1804.             move.l  (SP)+,dlgwindow(A6) ; save the dialog's handle
  1805.  
  1806. ;  Now do the dialog stuff
  1807.  
  1808.             subq.l  #2,sp               ; result on stack
  1809.             clr.l   -(sp)               ; normal filterproc
  1810.             pea     4(sp)               ; point to result space
  1811.             _ModalDialog                ;    Do it
  1812.  
  1813. ; discard dialog
  1814.  
  1815.             move.l  dlgWindow(a6),-(sp) ; point to dialog
  1816.             _DisposDialog
  1817.  
  1818. ; What did they hit?
  1819.  
  1820.             move.w  (sp)+,d0            ; get the button's item #
  1821.             cmp.w   #1,D0               ; (Try Again or OK (=1)) or Use New?
  1822.             beq.s   @10                 ; go if not "Use New"
  1823.             clr.b   SysLAPAddr(a2)      ; otherwise, Use New
  1824.             clr.w   SysNetNum(a2)       ;   and reset net and node adrs
  1825.  
  1826. @10         neg.l   D0                  ; item will be 1 or 2; return -4001
  1827.             sub.l   #4000,D0            ;     or -4002 as the status
  1828.             move.w  D0,warning(A6)      ; save it 
  1829.  
  1830. ; discard resource file
  1831.  
  1832.             move.w  resfile(A6),D0      ; get the refnum
  1833.             cmp.w   MyCurMap(A6),D0     ; was it the current resource file
  1834.             beq.s   @15                 ; go if so (someone else opened it)
  1835.             move.w  D0,-(sp)            ; else, push it
  1836.             CloseResFile                ; and close it
  1837. @15         move.w  warning(A6),D0      ; get the status from the NNNN
  1838.  
  1839. @20  ; D0 is result code for this routine
  1840.            _SUBEND 'DOWARN  '           ; and exit
  1841.  
  1842. FileName    DC.B    15
  1843.             DC.B    'Async AppleTalk'
  1844.             DC.B    'V1.2a6' 
  1845.             ALIGN   2
  1846.  
  1847. ; ****** end of lap.a
  1848.  
  1849.  
  1850.  
  1851.  
  1852.  
  1853.  
  1854.  
  1855.  
  1856.  
  1857.  
  1858. Listing 2
  1859.  
  1860. CRC Calculations
  1861.  
  1862.      This file contains a CRC calculation in Pascal.  It was used with
  1863.      preliminary versions of Async AppleTalk, and computes the same 
  1864.      function as the code in the M68000 listing.
  1865.  
  1866.      The NextCRC algorithm simulates the feedback shift register which
  1867.      normally implements a CRC calculation.  NextCRC takes each four-
  1868.      bit nibble of the input char and uses a table (crctbl) to select
  1869.      a mask which is exclusive-or'd with the current CRC accumulator.
  1870. }
  1871.  
  1872. { pseudo-CONST -- put this in the initialization code of your program
  1873.  
  1874. crctbl[00] := $0000; crctbl[01] := $CC01;
  1875. crctbl[02] := $D801; crctbl[03] := $1400;
  1876. crctbl[04] := $F001; crctbl[05] := $3C00;
  1877. crctbl[06] := $2800; crctbl[07] := $E401;
  1878. crctbl[08] := $A001; crctbl[09] := $6C00;
  1879. crctbl[10] := $7800; crctbl[11] := $B401;
  1880. crctbl[12] := $5000; crctbl[13] := $9C01;
  1881. crctbl[14] := $8801; crctbl[15] := $4400;
  1882. }
  1883.  
  1884. VAR crctbl : array [0..15] of integer;
  1885.  
  1886. function NextCRC (crc : integer; c : QDbyte) : integer;
  1887.  
  1888. VAR
  1889.      j : integer;
  1890.  
  1891. BEGIN
  1892.      j := crctbl[ band(bxor(crc,c),$000F) ];
  1893.      crc := bxor(bsr(crc,4),j);
  1894.      c := bsr(c,4);
  1895.      j := crctbl[ band(bxor(crc,c),$000F) ];
  1896.      crc := bxor(bsr(crc,4),j);
  1897.      nextcrc := crc;
  1898. END; { NextCRC }
  1899.  
  1900. function crc16 (p : qdptr; len : integer) : integer;
  1901. VAR
  1902.    i,j : integer;     { sixteen bits wide }
  1903.    c   : qdbyte;      { an eight bit value }
  1904.    crc : integer;     { the CRC accumulator }
  1905.  
  1906. BEGIN
  1907.    crc := 0;
  1908.    for i := 1 to len do begin
  1909.        c := p^;
  1910.        p := pointer(ord(p) + 1);
  1911.        crc := NextCRC(crc,c);
  1912.    end;
  1913.    crc16 := crc;
  1914. END; { crc16 }
  1915.  
  1916.  
  1917.  
  1918.  
  1919.  
  1920. Listing 3
  1921.  
  1922. ;
  1923. ; _AssumeEq  Arg1, Arg2 -- macro to generate a compile-time error if two
  1924. ;                          arguments are unequal.
  1925. ;
  1926. ;         To optimize code size, we will be making various assumptions,
  1927. ;         mainly as to offset values. This macro is a way of formalizing
  1928. ;         those assumptions within the code.
  1929. ;
  1930.  
  1931.     BLANKS ON
  1932.     STRING ASIS
  1933.  
  1934.           MACRO              
  1935.           _AssumeEq
  1936.                IF  &Eval(&Syslst[1]) <> &Eval(&Syslst[2]) THEN
  1937.                     _ERR           ; Invalid statement - will cause error
  1938.                ENDIF
  1939.           ENDM 
  1940. ;
  1941. ; _StatCount Arg1 -- increment a statistics count if stat keeping is enabled
  1942. ;
  1943. ;         Assumes A2 points to the driver variables
  1944. ;
  1945.  
  1946.           MACRO               
  1947.           _StatCount
  1948.                IF  debug THEN
  1949.                     ADDQ.L  #1,&Syslst[1](A2); Update the count
  1950.                ELSE 
  1951. .*                   nop   ; commented out
  1952.                ENDIF
  1953.           ENDM 
  1954.  
  1955. ;
  1956. ; _Subr        -- assembles a "Link A6,#???"
  1957. ;                 works for _SUBR <no param>   and  _SUBR ###
  1958. ;
  1959.           MACRO          
  1960.           _Subr     &size
  1961.           IF        &size = '' THEN
  1962.           Link      A6,#0
  1963.           ELSE 
  1964.           Link      A6,#(-&size)
  1965.           ENDIF
  1966.           ENDM 
  1967.  
  1968. ;
  1969. ; _Subend  NAME,$xx  -- Subroutine epilog
  1970. ;                         If debugging, put in Unlk and the name
  1971. ;
  1972.           MACRO            
  1973.           _Subend    &name
  1974.           Unlk      A6        ; unlink the stack frame
  1975.           rts                 ; and return
  1976.           DC.B      &name     ; the name
  1977.           ALIGN     2
  1978.           ENDM
  1979.  
  1980.